#include<iostream>
#include<string>
#include<cstdlib>
#include<vector>
#include<unistd.h>
#include<ctime>
#include<sys/wait.h>
#include"task.hpp"
using namespace std;

enum
{
    UasgeError = 1,
    ArgError,
    pipeError
};
void Usage(const std::string &proc)
{
        cout<<"Usage"<<proc<<"subporcess-num"<<endl;
}
class Channel
{
    public:
    Channel(int wfd,pid_t sub_id,const std::string &name)
    :_wfd(wfd)
    ,_sub_process_id(sub_id)
    ,_name(name)
    {}
    void PrintfDebug()
    {
        cout<<"_wfd"<<_wfd;
        cout<<"_sub_process_id"<<_sub_process_id;
        cout<<"name"<<_name<<endl;
    }
    string name()
    {
        return _name;
    }
    int wfd()
    {
        return _wfd;
    }
    pid_t pid()
    {
        return _sub_process_id;
    }
    void Close()
    {
        close(_wfd);
    }
    ~Channel()
    {

    }
    private:
    int _wfd;
    pid_t _sub_process_id;
    string _name;
};
class processpool
{
    public:
    processpool(int sub_process_num)
    :_sub_process_num(sub_process_num)
    {}
    //回调函数
    int Createprocess(work_t work)
    {
        std::vector<int> fds;
        for (int number  = 0; number < _sub_process_num; number++)
        {
            int pipefd[2]{0};
            int n  = pipe(pipefd);
            if(n<0)
            {
                return pipeError;
            }
            //传进子进程
             pid_t id = fork();
             if(id==0)
             {
               if(!fds.empty())
               {
                std::cout<<"close w fd:";
                for(auto fd: fds)
                {
                    close(fd);
                    std::cout<<fd<<endl;
                }
               }
               close(pipefd[1]);
               dup2(pipefd[0],0);
               work(pipefd[0]);
                exit(0);
             }
             string cname = "channel-"+ to_string(number);
             close(pipefd[0]);
             channels.push_back(Channel(pipefd[1],id,cname));
        }
        return 0;
    }
    int NextChannle()
    {
        static int next = 0;
        int c = next;
        next++;
        next%=channels.size();
        return c;
    }
    void SendTaskCode(int index,uint32_t code)
    {
        cout<<"send code"<<code<<"to"<<channels[index].name()<<"sub prorcess id:"<<channels[index].pid()<<endl;
        write(channels[index].wfd(),&code,sizeof(code));
    }
    //让子进程全部退出，只需要关闭所有channel w即可；
    void KillALL()
    {
        for(auto &channel:channels)
        {
            channel.Close();
            pid_t pid = channel.pid();
            pid_t rid = waitpid(rid,nullptr,0);
            if(rid ==pid)
            {
                std::cout<<"wait sub process: "<<pid<<"success..."<<std::endl;
            }
            std::cout<<channel.name()<<"close done"<<"sub process quit now :"<<channel.pid()<<std::endl;
        }
    }
    void wait()
    {

    }
    void Debug()
    {
        for (auto &channel :channels)
        {
            channel.PrintfDebug();
        }
        
    }
    ~processpool()
    {

    }
    private:
    int _sub_process_num;
    vector<Channel> channels;
};
void CtrlProcesspool(processpool *processpool_ptr,int cnt)
{
    while (cnt)
    {
        //选择一个通道
        int channel = processpool_ptr->NextChannle();
        //选择任务
        uint32_t code = NextTask();
        //发送任务
        processpool_ptr->SendTaskCode(channel,code);
        sleep(1);
        cnt--;
    }
}
int main(int argc,char *argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        return ArgError;
    }
    int sub_process_num = std::stoi(argv[1]);
    if(sub_process_num<=0)
    {
        return ArgError;
    }
    srand((uint64_t)time(nullptr));
    //创建通信信管道
    processpool *processpool_prt = new processpool(sub_process_num);
    processpool_prt->Createprocess(worker);
    processpool_prt->Debug();
    //2:控制子进程
    CtrlProcesspool(processpool_prt,10);
    std::cout<<"task run dnoe"<<std::endl;
    //3:回收子进程和子进程退出；
    processpool_prt->KillALL();
    //让所有已经推出的子进程把他进程资源回收wait/waitpid
    processpool_prt->wait();
    delete processpool_prt;
    return 0;
}
